
繼第 4 天的「162. Find Peak Element」,今天來解 15 這題!還沒看過第 4 天或再之前天數的朋友,歡迎讀讀~
這題是這系列第 1 天 「1. Two Sum」 的進階題,歡迎也前往那篇讀讀。
話不多說,我們開始吧!
給予一個 尚未排序 的陣列 nums ,請回傳 和為 0 的所有組合,這些組合不得重複。
nums = [-1, 0, 1, 2, -1, -4]
所有組合:
[[-1,-1,2],[-1,0,1]]
當要在 已排序 找到期待的 數對 的時候,就可以利用 2 pointers 技巧。
在這裡我們可以從最左和最右往中間趨近。
示意:
  f
[-4, -1, -1, 0, 1, 2]
      ^ -->    <-- ^
      left         right
先來建立基本的條件
class Solution {
    func threeSum(_ nums: [Int]) -> [[Int]] {
        // 當 nums 小於 3 個元素,就直接回傳空陣列
        guard nums.count >= 3 else { return [] }
        // 排序陣列
        let nums = nums.sorted(by: <)
        var results: [[Int]] = []
        // 待實作
        return results
    }
}
class Solution {
    func threeSum(_ nums: [Int]) -> [[Int]] {
        guard nums.count >= 3 else { return [] }
        let nums = nums.sorted(by: <)
        var results: [[Int]] = []
        
+        // 由於需要比較三個元素,因此只需要走訪到倒數第 3 個位置
+        for index in 0..<(nums.count-2) {
+            // 為了避免有重複結果
+            // 因此當走訪到相同值時則跳過
+            if index == 0 || nums[index] != nums[index-1] {
+                // 待實作
+            }
+        }
        return results
    }
}
class Solution {
    func threeSum(_ nums: [Int]) -> [[Int]] {
        guard nums.count >= 3 else { return [] }
        let nums = nums.sorted(by: <)
        var results: [[Int]] = []
        for index in 0..<(nums.count-2) {
            if index == 0 || nums[index] != nums[index-1] {
+               let target = -nums[index]
+               // 初始在主 pointer 的右 1
+               var left = index + 1
+               // 初始在尾端
+               var right = nums.count - 1
+
+               // 待實作
            }
        }
        return results
    }
}
class Solution {
    func threeSum(_ nums: [Int]) -> [[Int]] {
        guard nums.count >= 3 else { return [] }
        let nums = nums.sorted(by: <)
        var results: [[Int]] = []
        for index in 0..<(nums.count-2) {
            if index == 0 || nums[index] != nums[index-1] {
                let target = -nums[index]
                var left = index + 1
                var right = nums.count - 1
+                while left < right  {
+                    let sum = nums[left] + nums[right]
+                    if sum == target {
+                        results.append([nums[index], nums[left], nums[right]])
+
+                        // 跳過重複的數值
+                        while left < right && nums[left] == nums[left + 1] { left += 1 }
+                        while left < right && nums[right] == nums[right - 1] { right -= 1 }
+
+                        // 指標往中間移動
+                        left += 1
+                        right -= 1
+                    }
+                } else if sum > target {
+                    // 待實作  
+                } else {
+                   // 待實作
+                }
            }
        }
        return results
    }
}
class Solution {
    func threeSum(_ nums: [Int]) -> [[Int]] {
        guard nums.count >= 3 else { return [] }
        let nums = nums.sorted(by: <)
        var results: [[Int]] = []
        
        for index in 0..<(nums.count-2) {
            if index == 0 || nums[index] != nums[index-1] { 
                let target = -nums[index]
                var left = index + 1
                var right = nums.count - 1
                while left < right {
                    let sum = nums[left] + nums[right]
                    if sum == target {
                        results.append([nums[index], nums[left], nums[right]])
                        while left < right && nums[left] == nums[left + 1] { left += 1 }
                        while left < right && nums[right] == nums[right - 1] { right -= 1 }
                        left += 1
                        right -= 1
                    } else if sum > target {
+                        right -= 1
                    } else {
+                        left += 1
                    }
                }
            }
        }
        return results
    }
}
完成後的程式碼如下:
class Solution {
    func threeSum(_ nums: [Int]) -> [[Int]] {
        guard nums.count >= 3 else { return [] }
        let nums = nums.sorted(by: <)
        var results: [[Int]] = []
        
        for index in 0..<(nums.count-2) {
            if index == 0 || nums[index] != nums[index-1] { 
                let target = -nums[index]
                var left = index + 1
                var right = nums.count - 1
                while left < right {
                    let sum = nums[left] + nums[right]
                    if sum == target {
                        results.append([nums[index], nums[left], nums[right]])
                        while left < right && nums[left] == nums[left + 1] { left += 1 }
                        while left < right && nums[right] == nums[right - 1] { right -= 1 }
                        left += 1
                        right -= 1
                    } else if sum > target {
                        right -= 1
                    } else {
                        left += 1
                    }
                }
            }
        }
        return results
    }
}
令 n 為 nums 的總數。
| Big O | 說明 | |
|---|---|---|
| 時間複雜度 | O(n^2) | 外層的 for loop 和內層的 while 為 O(n^2) ,排序為 O(nlogn) 。總和之後雖然為 O(n^2 + nlogn) 取最高次方簡化為 O(n^2) | 
| 空間複雜度 | O(n) | Swift 排序後會用到新的記憶體空間 | 
3Sum 這題從程式碼邊看邊講解算容易,但是先用文字簡要清楚說明真的不簡單,不過還是試著寫寫看。如果有任何回饋或是文中寫不清楚的地方,歡迎在下面留言!
今天就到這裡,明天見!